package com.smallframework.mvc;


import com.alibaba.fastjson.JSON;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Request处理的容器
 * 保存url路径 和controller实例，方法的映射关系
 * 对request进行处理
 * Created by tan on 2017/7/4.
 */
public class RequestHandlerContainer {
	private IOCContainer iocContainer;
	private Map<String, Method> pathMethodMap = new HashMap<>();
	private Map<String, Object> pathObjectMap = new HashMap<>();

	private static Set<Class<?>> primitiveClassSet;

	//这里 我想了很久
	private static HashMap<Class<?>, Converter<String, ?>> stringConverterMap;

	static {
		primitiveClassSet = new HashSet<>();
		primitiveClassSet.add(byte.class);
		primitiveClassSet.add(char.class);
		primitiveClassSet.add(short.class);
		primitiveClassSet.add(int.class);
		primitiveClassSet.add(long.class);
		primitiveClassSet.add(float.class);
		primitiveClassSet.add(double.class);
		primitiveClassSet.add(boolean.class);

		//TODO 添加其他的converter
		stringConverterMap = new HashMap<>();
		stringConverterMap.put(Integer.class, new StringIntegerConverter());
		stringConverterMap.put(int.class, new StringIntegerConverter());
	}

	public RequestHandlerContainer(IOCContainer iocContainer) {
		this.iocContainer = iocContainer;
	}

	public void init() {
		dealRequestMapping();
	}


	private void dealRequestMapping() {
		List<Class<? extends Annotation>> annotationClasses = new ArrayList<>();
		annotationClasses.add(Component.class);
		annotationClasses.add(Controller.class);
		List<Class<?>> classes = iocContainer.getClassHelper().getClassesByAnnotations(annotationClasses);
		for (Class<?> clazz : classes) {
			RequestMapping controllerMapping = clazz.getDeclaredAnnotation(RequestMapping.class);

			String controllerPath = "";
			if (controllerMapping != null) {
				controllerPath = controllerMapping.value();
			}
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				RequestMapping mapping = method.getDeclaredAnnotation(RequestMapping.class);
				if (mapping != null) {
					String requestPath = controllerPath + mapping.value();
					pathMethodMap.put(requestPath, method);
					pathObjectMap.put(requestPath, iocContainer.getBean(clazz));
				}
			}
		}
	}

	public void dealRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		//获取当前的方法和路径
		String requestMethod = req.getMethod().toLowerCase();
		String requestPath = req.getServletPath();
		Enumeration<String> names = req.getParameterNames();


		Method method = pathMethodMap.get(requestPath);
		if (method == null) {
			//异常
			throw new Exception("404");
		}
		//获取method的参数名。 spring是如何获取到的?
		//TODO 反射获取一个方法中的参数名（不是类型）  http://www.cnblogs.com/guangshan/p/4660564.html
		//spring中expression表达式的参数名字是如何获取到的？  https://www.zhihu.com/question/30386449
		//获取方法参数名问题  http://bbs.csdn.net/topics/391054482?page=1
		//Java——通过反射获取函数参数名称  http://blog.csdn.net/revitalizing/article/details/71036970
		//Parameter不行，parameter.getName()获取到的参数名字是arg0....
		//Parameter[] parameters = method.getParameters();
		Class<?>[] paramTypes = method.getParameterTypes();
		String[] paramNames = ClassUtil.getMethodParamNames(method);
		Object[] params = new Object[paramNames.length];
		int index = 0;
		for (Class<?> paramClazz : paramTypes) {
			if (paramClazz.equals(HttpServletRequest.class)) {
				params[index++] = req;
			} else if (paramClazz.equals(HttpServletResponse.class)) {
				params[index++] = resp;
			} else {
				String name = paramNames[index];
				Object paramValue = tranferFromStringToClazz(req.getParameter(name), paramClazz);
				params[index++] = paramValue;
			}
		}

		Object object = pathObjectMap.get(requestPath);

		if (method != null) {
			Object result = method.invoke(object, params);
			render(result, method, req, resp);
		} else {
			//异常
			throw new Exception("404");
		}
	}

	private void render(Object result, Method method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		ResponseBody responseBody = method.getAnnotation(ResponseBody.class);
		if (responseBody != null) {
			resp.setContentType("application/json");

			PrintWriter out = resp.getWriter();
			out.println(JSON.toJSON(result));

		} else {
			//TODO
			if (result instanceof String) {
				String jspRootPath = "/WEB-INF/";
				String jspPath = jspRootPath + result;
				req.getRequestDispatcher(jspPath).forward(req, resp);
			} else {

			}
		}
	}

	private Object tranferFromStringToClazz(String paramValue, Class<?> paramClazz) throws Exception {
		if (paramValue == null && primitiveClassSet.contains(paramClazz)) {
			throw new Exception("参数不存在");
		}

		if (paramClazz.equals(String.class)) {
			return paramValue;
		}

		Converter<String, ?> converter = stringConverterMap.get(paramClazz);
		if (converter == null) {
			throw new Exception("not support " + paramClazz.getName() + "convert");
		} else {
			Object value = converter.convert(paramValue);
			return value;
		}
	}
}
